package com.chuangke.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.chuangke.common.constant.SysRelationGraphType;
import com.chuangke.common.trans.annotation.TransResult;
import com.chuangke.system.dao.SysRelationGraphDao;
import com.chuangke.system.entity.SysRelationGraph;
import com.chuangke.system.service.SysRelationGraphService;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@Service
public class SysRelationGraphServiceImpl implements SysRelationGraphService {

	@Autowired
	private SysRelationGraphDao sysRelationGraphDao;

	@Override
	public void save(List<SysRelationGraph> records) {
		sysRelationGraphDao.saveOrUpdateBatch(records);
	}

	@Override
	public void deleteByIds(List<String> ids) {
		sysRelationGraphDao.removeBatchByIds(ids);
	}

	@Override
	public List<SysRelationGraph> findList() {
		return sysRelationGraphDao.list();
	}

	@Override
	public List<SysRelationGraph> findListByTargetId(List<String> ids) {
		QueryWrapper<SysRelationGraph> qw = new QueryWrapper<>();
		qw.lambda().in(SysRelationGraph::getTarget, ids);
		return sysRelationGraphDao.list(qw);
	}

	@Override
	public List<SysRelationGraph> findListBySourceId(List<String> ids) {
		QueryWrapper<SysRelationGraph> qw = new QueryWrapper<>();
		qw.lambda().in(SysRelationGraph::getSource, ids);
		return sysRelationGraphDao.list(qw);
	}

	@Override
	public void deleteByObjectId(List<String> objectIds) {
		QueryWrapper<SysRelationGraph> qw = new QueryWrapper<>();
		qw.lambda().in(SysRelationGraph::getSource, objectIds).or().in(SysRelationGraph::getTarget, objectIds);
		sysRelationGraphDao.remove(qw);
	}

	@Override
	public void delete(Wrapper<SysRelationGraph> queryWrapper) {
		sysRelationGraphDao.remove(queryWrapper);
	}

	@Override
	@TransResult
	public List<SysRelationGraph> findDeepRelationByObjectId(String objectId,Predicate predicate) {
		List<SysRelationGraph> list = new ArrayList<>() ;
		List<String> queryedIds = new ArrayList<>() ;
		List<String> unQueryIds = new ArrayList<>();//Arrays.asList(objectId) ;
		unQueryIds.add(objectId);
		cycleFind(queryedIds,unQueryIds,list,predicate) ;
		
		return list;
	}
	
	@Override
	@TransResult
	public List<SysRelationGraph> findDirectRelationByObjectId(String objectId,Predicate predicate) {
		return find(Collections.singletonList(objectId),predicate);
	}

	@Override
	@TransResult
	public List<SysRelationGraph> findDirectDeepRelationByObjectId(String objectId,Predicate predicate) {
		List<SysRelationGraph> list = find(Collections.singletonList(objectId),predicate);
		if(CollectionUtils.isEmpty(list)) {
			return list;
		}

		Map<String,SysRelationGraph> map = list.stream().collect(Collectors.toMap(srg -> srg.getSource()+"-"+srg.getTarget(),srg -> srg)) ;

		List<String> parentIds = new ArrayList<>() ;
		List<String> sonIds = new ArrayList<>() ;
		for(SysRelationGraph srg : list) {
			if(!SysRelationGraphType.RELATION_TYPE_CJ.equals(srg.getRelation())){
				continue;
			}

			if(srg.getSource().equals(objectId)) {
				parentIds.add(srg.getTarget()) ;
			}else{
				sonIds.add(srg.getSource()) ;
			}
		}

		findParent(map,parentIds,predicate) ;
		findSon(map,sonIds,predicate) ;

		return new ArrayList<>(map.values());
	}

	private void findParent(Map<String,SysRelationGraph> map,List<String> parentIds, Predicate predicate) {
		if(CollectionUtils.isEmpty(parentIds)) {
			return ;
		}
		List<SysRelationGraph> parentList = find(parentIds,predicate);

		parentList = parentList.stream().filter(srg -> parentIds.contains(srg.getSource()) &&
				SysRelationGraphType.RELATION_TYPE_CJ.equals(srg.getRelation())).collect(Collectors.toList()) ;

		if(CollectionUtils.isEmpty(parentList)) {
			return ;
		}

		parentIds.clear();

		parentList.forEach(srg -> {
			if(!map.containsKey(srg.getSource()+"-"+srg.getTarget())){
				map.put(srg.getSource()+"-"+srg.getTarget(),srg) ;
				parentIds.add(srg.getTarget()) ;
			}
		}) ;
		findParent(map,parentIds,predicate) ;
	}

	private void findSon(Map<String,SysRelationGraph> map,List<String> sonIds, Predicate predicate) {
		if(CollectionUtils.isEmpty(sonIds)) {
			return ;
		}
		List<SysRelationGraph> sonList = find(sonIds,predicate);

		sonList = sonList.stream().filter(srg -> sonIds.contains(srg.getTarget()) &&
				SysRelationGraphType.RELATION_TYPE_CJ.equals(srg.getRelation())).collect(Collectors.toList()) ;

		if(CollectionUtils.isEmpty(sonList)) {
			return ;
		}

		sonIds.clear();

		sonList.forEach(srg -> {
			if(!map.containsKey(srg.getSource()+"-"+srg.getTarget())){
				map.put(srg.getSource()+"-"+srg.getTarget(),srg) ;
				sonIds.add(srg.getSource()) ;
			}
		}) ;
		findSon(map,sonIds,predicate) ;
	}
	
	private void cycleFind(List<String> queryedIds,List<String> unQueryIds,List<SysRelationGraph> list,Predicate predicate) {
		List<SysRelationGraph> clist = find(unQueryIds,predicate);
		if(CollectionUtils.isEmpty(clist)) {
			return ;
		}
		
		add(list,clist) ;
		queryedIds.addAll(unQueryIds) ;
		unQueryIds.clear();
		
		clist.forEach(srg ->{
			if(!queryedIds.contains(srg.getSource())) {
				unQueryIds.add(srg.getSource()) ;
			}
			if(!queryedIds.contains(srg.getTarget())) {
				unQueryIds.add(srg.getTarget()) ;
			}
		});

		if(!unQueryIds.isEmpty()){
			cycleFind(queryedIds,unQueryIds,list,predicate) ;
		}
	}
	
	private void add(List<SysRelationGraph> list,List<SysRelationGraph> clist) {
		Map<String,SysRelationGraph> map = list.stream().collect(Collectors.toMap(srg -> srg.getSource()+"-"+srg.getTarget(), a -> a));
		Map<String,SysRelationGraph> cmap = clist.stream().collect(Collectors.toMap(srg -> srg.getSource()+"-"+srg.getTarget(), a -> a));
		
		cmap.keySet().forEach(k ->{
			if(!map.containsKey(k)) {
				list.add(cmap.get(k)) ;
			}
		});
	}

	/**
	 *
	 * @param objectIds
	 * @param predicate 过滤条件
	 * @return
	 */
	private List<SysRelationGraph> find(List<String> objectIds, Predicate predicate){
		QueryWrapper<SysRelationGraph> qw = new QueryWrapper<>();
		qw.lambda().in(SysRelationGraph::getSource, objectIds).or().in(SysRelationGraph::getTarget, objectIds);
		List<SysRelationGraph> list = sysRelationGraphDao.list(qw);
		if(predicate == null){
			return list ;
		}
		return (List<SysRelationGraph>) list.stream().filter(predicate).collect(Collectors.toList()) ;
	}

}
